package com.acme.bankapp.domain.account;

import com.acme.bankapp.exception.BankException;
import com.acme.bankapp.exception.IllegalAccountCreationException;
import com.acme.bankapp.exception.NotEnoughFundsException;

import java.util.Scanner;

public abstract class AbstractAccount implements Account {
    private double balance;

    public AbstractAccount (double initialBalance) throws IllegalAccountCreationException {
        if (initialBalance < 0) {
            throw new IllegalAccountCreationException();
        }
        this.balance = initialBalance;
    }

    @Override
    public double getBalance() {
        return balance;
    }

    @Override
    public void deposit(double x) {
        balance += Math.abs(x);
    }

    @Override
    public void withdraw(double x) throws NotEnoughFundsException {
        x = Math.abs(x);
        if (x <= maximumAmountToWithdraw()) {
            balance -= x;
        } else {
            throw new NotEnoughFundsException(x);
        }
        assert getBalance() >= 0 : "balance < 0";
    }

    @Override
    public double maximumAmountToWithdraw() {
        return getBalance();
    }

    @Override
    public String toJSONString() {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("accounttype=");
        if (this instanceof CheckingAccount) {
            stringBuilder.append('c');
        } else {
            stringBuilder.append('s');
        }
        stringBuilder.append(";balance=").append(getBalance()).append(";overdraft=");
        if (this instanceof CheckingAccount) {
            stringBuilder.append(((CheckingAccount) this).getOverdraft()).append(";");
        } else {
            stringBuilder.append("0").append(";");
        }
        return stringBuilder.toString();
    }

    public static Account parseJSON(String s) throws BankException {
        Account account;

        Scanner accountString = new Scanner(s).useDelimiter("[=;]");
        accountString.next();
        String accountType = accountString.next();
        accountString.next();
        double balance = Double.parseDouble(accountString.next());
        accountString.next();
        double overdraft = Double.parseDouble(accountString.next());
        switch (accountType) {
            case "s":
                account = new SavingAccount(balance);
                break;
            case "c":
                account = new CheckingAccount(balance, overdraft);
                break;
            default:
                throw new BankException();
        }

        return account;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        AbstractAccount that = (AbstractAccount) o;

        if (Double.compare(that.balance, balance) != 0) return false;

        return true;
    }

    @Override
    public int hashCode() {
        long temp = Double.doubleToLongBits(balance);
        return (int) (temp ^ (temp >>> 32));
    }

    @Override
    public String toString() {
        return "[" + getClass().getSimpleName() + ": balance=" + balance + "]";
    }
}
